using System;
using System.Collections;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using Microsoft.Windows.SyncManager.Interop;

namespace iPodShuffle
{
	public abstract class SyncMgrSynchronizeBase: ISyncMgrSynchronize
	{
		protected SYNCMGRFLAG					m_SyncMgrFlags;
		protected SYNCMGRITEM[]					m_Items;
		protected Guid[]						m_SelectedItemIDs;
		protected Hashtable						m_ItemStatus = new Hashtable();
		protected ISyncMgrSynchronizeCallback	m_Callback;

		#region ISyncMgrSynchronize Members

		public void Initialize(uint reserved, SYNCMGRFLAG syncMgrFlags, uint cookieSize, byte[] cookie)
		{
			m_SyncMgrFlags = syncMgrFlags;
		}

		public void EnumSyncMgrItems(out ISyncMgrEnumItems itemEnum)
		{
			try
			{
				SetItems( CreateItems() );
				itemEnum = new FileSyncMgrEnumItems( m_Items ) ;
			}
			catch (Exception ex)
			{
				LogError(ex);
				throw;
			}
		}

		public virtual void PrepareForSync(int numItems, Guid[] selectedItemIDs, IntPtr parentWindow, uint reserved)
		{
			m_SelectedItemIDs = selectedItemIDs;
			m_Callback.PrepareForSyncCompleted(0);
		}

		public virtual void SetItemStatus(ref Guid itemID, SYNCMGRSTATUS status)
		{
			bool bAllItems = Guid.Empty == itemID;

			if ( bAllItems )
			{
				foreach ( SYNCMGRITEM item in m_Items )
				{
					m_ItemStatus[item.ItemID] = status;
					NotifyProgress( item.ItemID, status );
				}
			}
			else
			{
				m_ItemStatus[itemID] = status;
				NotifyProgress( itemID, status );
			}
		}

		public void SetProgressCallback(ISyncMgrSynchronizeCallback callback )
		{
			m_Callback = callback;
		}

		public virtual void ShowError(IntPtr parentWindow, ref Guid errorID)
		{
			throw new NotImplementedException();
		}

		public virtual void ShowProperties(IntPtr parentWindow, ref Guid itemID)
		{
			throw new NotImplementedException();
		}

		public virtual void Synchronize(IntPtr parentWindow)
		{
			// this can be run from another thread 

			try
			{
				if (null != m_SelectedItemIDs)
					foreach (Guid selectedItemID in m_SelectedItemIDs)
						if ( SYNCMGRSTATUS.PENDING == GetItemStatus( selectedItemID ) )
							Synchronize( GetItem(selectedItemID), parentWindow );
			}
			catch (Exception ex)
			{
				LogError(ex);
				throw;
			}
			finally
			{
				m_Callback.SynchronizeCompleted(0);
			}
		}

		public abstract void GetHandlerInfo(out SYNCMGRHANDLERINFO info);

		public virtual void GetItemObject(ref Guid itemID, ref Guid guid, out IntPtr item)
		{
			// MSDN:
			// This method is for future use. 
			// There are currently no interfaces defined on an Item. 
			// Application implementers must return E_NOTIMPL from this method.
			throw new NotImplementedException(); 
		}

		#endregion

		public virtual void LogError( Exception error )
		{
			LogError( error.Message );
		}
		public virtual void LogError( string text )
		{
			if ( null != m_Callback )
			{
				m_Callback.LogError(
					SYNCMGRLOGLEVEL.SYNCMGRLOGLEVEL_ERRORS
					, text
					, null );
			}
		}

		public virtual void LogInfo( string text )
		{
			if ( null != m_Callback )
			{
				m_Callback.LogError(
					SYNCMGRLOGLEVEL.SYNCMGRLOGLEVEL_INFORMATION
					, text
					, null );
			}
		}

		public virtual void LogWarning( string text )
		{
			if ( null != m_Callback )
			{
				m_Callback.LogError(
					SYNCMGRLOGLEVEL.SYNCMGRLOGLEVEL_WARNING
					, text
					, null );
			}
		}

		protected SYNCMGRSTATUS GetItemStatus( Guid itemID )
		{
			return (SYNCMGRSTATUS) m_ItemStatus[itemID];
		}

		protected void SetItems( SYNCMGRITEM[] items )
		{
			m_Items   = items;
		
			// init
			m_ItemStatus.Clear();
			m_SelectedItemIDs = null;

			for(int i=0; i < m_Items.Length; i++)
				m_ItemStatus[ m_Items[i].ItemID ] = SYNCMGRSTATUS.PENDING;
		}

		protected SYNCMGRITEM GetItem( Guid itemID )
		{
			foreach( SYNCMGRITEM item in m_Items)
				if ( item.ItemID == itemID )
					return item;

			throw new IndexOutOfRangeException();
		}

		protected void NotifyProgress( Guid itemID, SYNCMGRSTATUS status )
		{
			if ( null != m_Callback )
			{
				SyncMgrProgressItem progress = new SyncMgrProgressItem();
				progress.Size		= Marshal.SizeOf( typeof(SyncMgrProgressItem) );
				progress.Mask		= SYNCMGRPROGRESSITEMMASK.STATUSTYPE;
				progress.StatusType	= status;
			
				m_Callback.Progress( ref itemID, ref progress );
			}
		}

		protected void NotifyProgress( Guid itemID, string text )
		{
			if ( null != m_Callback )
			{
				SyncMgrProgressItem progress = new SyncMgrProgressItem();
				progress.Size		= Marshal.SizeOf( typeof(SyncMgrProgressItem) );
				progress.Mask		= SYNCMGRPROGRESSITEMMASK.STATUSTEXT;
				progress.StatusText = text;
			
				m_Callback.Progress( ref itemID, ref progress );
			}
		}

		protected void NotifyProgress( Guid itemID, string text, SYNCMGRSTATUS status )
		{
			if ( null != m_Callback )
			{
				SyncMgrProgressItem progress = new SyncMgrProgressItem();
				progress.Size		= Marshal.SizeOf( typeof(SyncMgrProgressItem) );
				progress.Mask		= SYNCMGRPROGRESSITEMMASK.STATUSTEXT
									| SYNCMGRPROGRESSITEMMASK.STATUSTYPE;
				progress.StatusText = text;
				progress.StatusType = status;
			
				m_Callback.Progress( ref itemID, ref progress );
			}
		}

		public abstract SYNCMGRITEM[] CreateItems();
		public abstract void Synchronize(SYNCMGRITEM item, IntPtr parentWindow);

		/// <summary>
		/// Called when derived class is registered as a COM server.
		/// </summary>
		[ComRegisterFunction]
		public static void Register(Type t)
		{
			// get handler name
			SYNCMGRHANDLERINFO handlerInfo;
			ISyncMgrSynchronize mgr = (ISyncMgrSynchronize) Activator.CreateInstance(t);
			mgr.GetHandlerInfo( out handlerInfo );
			string name = handlerInfo.HandlerName;

			Registry.LocalMachine.CreateSubKey(
				@"SOFTWARE\Microsoft\Windows\CurrentVersion\Syncmgr\Handlers\"+t.GUID.ToString("B")
				).SetValue(null, name);
		}

		/// <summary>
		/// Called when derived class is unregistered as a COM server.
		/// </summary>
		[ComUnregisterFunction]
		public static void Unregister(Type t)
		{
			string guid = t.GUID.ToString("B");

			if ( null != Registry.LocalMachine.OpenSubKey(
				@"SOFTWARE\Microsoft\Windows\CurrentVersion\Syncmgr\Handlers\"+guid, false ) )
			{
				Registry.LocalMachine.OpenSubKey( 
					@"SOFTWARE\Microsoft\Windows\CurrentVersion\Syncmgr\Handlers", true 
					).DeleteSubKeyTree( guid );
			}
		}
	}
}
